export type TWinner = 'dealer' | 'player' | 'none';

export enum EDispatchType {
  reset,
  shuffle,
  start,
  hit,
  stand,
}

export type DispatchCommandType = {
  type: EDispatchType;
  payload?: any;
}

export type CardType = {
  points: number;
  row: number;
  col: number;
  showBack?: boolean;
}

const ALL_CARDS: CardType[] = [
  { points:  1, row: 0, col:  0, },
  { points:  2, row: 0, col:  1, },
  { points:  3, row: 0, col:  2, },
  { points:  4, row: 0, col:  3, },
  { points:  5, row: 0, col:  4, },
  { points:  6, row: 0, col:  5, },
  { points:  7, row: 0, col:  6, },
  { points:  8, row: 0, col:  7, },
  { points:  9, row: 0, col:  8, },
  { points: 10, row: 0, col:  9, },
  { points: 10, row: 0, col: 10, },
  { points: 10, row: 0, col: 11, },
  { points: 10, row: 0, col: 12, },

  { points:  1, row: 1, col:  0, },
  { points:  2, row: 1, col:  1, },
  { points:  3, row: 1, col:  2, },
  { points:  4, row: 1, col:  3, },
  { points:  5, row: 1, col:  4, },
  { points:  6, row: 1, col:  5, },
  { points:  7, row: 1, col:  6, },
  { points:  8, row: 1, col:  7, },
  { points:  9, row: 1, col:  8, },
  { points: 10, row: 1, col:  9, },
  { points: 10, row: 1, col: 10, },
  { points: 10, row: 1, col: 11, },
  { points: 10, row: 1, col: 12, },

  { points:  1, row: 2, col:  0, },
  { points:  2, row: 2, col:  1, },
  { points:  3, row: 2, col:  2, },
  { points:  4, row: 2, col:  3, },
  { points:  5, row: 2, col:  4, },
  { points:  6, row: 2, col:  5, },
  { points:  7, row: 2, col:  6, },
  { points:  8, row: 2, col:  7, },
  { points:  9, row: 2, col:  8, },
  { points: 10, row: 2, col:  9, },
  { points: 10, row: 2, col: 10, },
  { points: 10, row: 2, col: 11, },
  { points: 10, row: 2, col: 12, },

  { points:  1, row: 3, col:  0, },
  { points:  2, row: 3, col:  1, },
  { points:  3, row: 3, col:  2, },
  { points:  4, row: 3, col:  3, },
  { points:  5, row: 3, col:  4, },
  { points:  6, row: 3, col:  5, },
  { points:  7, row: 3, col:  6, },
  { points:  8, row: 3, col:  7, },
  { points:  9, row: 3, col:  8, },
  { points: 10, row: 3, col:  9, },
  { points: 10, row: 3, col: 10, },
  { points: 10, row: 3, col: 11, },
  { points: 10, row: 3, col: 12, },
]

type GameStateType = {
  winners: TWinner[];
  unknownCards: CardType[];
  dealerCards: CardType[];
  playerCards: CardType[];
  dealerScore: number;
  playerScore: number;
  closed: boolean;
}

export const initialGameState: () => GameStateType = () => {
  return {
    winners: [],
    unknownCards: [],
    dealerCards: [],
    playerCards: [],
    dealerScore: 0,
    playerScore: 0,
    closed: false,
  }
}

const fnReset = (state: GameStateType) => {
  state.dealerCards.length = 0;
  state.playerCards.length = 0;
  state.unknownCards.length = 0;
  state.closed = false;
  ALL_CARDS.forEach((card) => {
    state.unknownCards.push({
      points: card.points,
      row: card.row,
      col: card.col,
      showBack: true,
    })
  })
}
const fnShuffle = (cards: CardType[]) => {
  cards.sort(() => (Math.random() - 0.5));
}

const fnPick = (source: CardType[], target: CardType[], count:number) => {
  for (let i=0;i<count;i++) {
    const c = source.shift();
    if (!!c) {
      c.showBack = false;
      target.push(c);
    }

  }
}
const checkPoints = (cards: CardType[]) => {
  const ps = cards.map(c => c.points)
  const sum = ps.reduce((total, cp) => (total + cp), 0);
  if (ps.includes(1) && (sum + 9) <= 21) {
    return sum + 10;
  }
  else return sum;
}
const fnCheckPoints = (state: GameStateType) => {
  state.dealerScore = checkPoints(state.dealerCards)
  state.playerScore = checkPoints(state.playerCards)
  if (state.playerScore === 21) {
    if (state.dealerScore === 21) {
      state.winners.push('none');
    } else {
      state.winners.push('player');
    }
    state.closed = true;
  } else if (state.dealerScore === 21) {
    state.winners.push('dealer');
    state.closed = true;
  } else if (state.playerScore > 21) {
    state.winners.push('dealer');
    state.closed = true;
  } else if (state.dealerScore > 21) {
    state.winners.push('player');
    state.closed = true;
  } else if (state.closed) {
    if (state.playerScore > state.dealerScore) {
      state.winners.push('player');
    } else if (state.playerScore < state.dealerScore) {
      state.winners.push('dealer');
    } else  {
      state.winners.push('none');
    } 
  } 
  if (state.closed) {
    state.dealerCards[0].showBack = false;
  }
}
const fnCheck = (state: GameStateType) => {
  while( checkPoints(state.dealerCards) < 17) {
    fnPick(state.unknownCards, state.dealerCards, 1);
  }
  state.closed = true;
  state.dealerCards[0].showBack = false;
}

export const reduceHandler: (currentState: GameStateType, command: DispatchCommandType) => GameStateType
= (currentState, command) => {
  switch(command.type) {
    case EDispatchType.reset:
      fnReset(currentState);
      break;
    case EDispatchType.shuffle:
      fnShuffle(currentState.unknownCards);
      break;
    case EDispatchType.start:
      fnPick(currentState.unknownCards, currentState.playerCards, 2);
      fnPick(currentState.unknownCards, currentState.dealerCards, 2);
      currentState.dealerCards[0].showBack = true;
      break;
    case EDispatchType.hit:
      fnPick(currentState.unknownCards, currentState.playerCards, 1);
      break;
    case EDispatchType.stand:
      fnCheck(currentState);
      break;
    default:
      return currentState;
  }

  fnCheckPoints(currentState);
  return { ...currentState }
}
